Set up
suppressPackageStartupMessages({
library(tidyverse)
library(readxl)
library(cDriver) # Calculate CCF, https://github.com/hanasusak/cDriver/
})
Load and process data
# Read maf
maf_df <- readr::read_tsv(maf_file, guess_max = 100000, show_col_types = FALSE) %>%
select(Kids_First_Participant_ID, Kids_First_Biospecimen_ID, cg_id, tumor_descriptor,
sample_id, aliquot_id, Chromosome,
Start_Position, Reference_Allele, Tumor_Seq_Allele1, Tumor_Seq_Allele2,
t_ref_count, t_alt_count, tmb,
tumor_fraction, tumor_ploidy, VAF, Hugo_Symbol,
mutation_count) %>%
# Remove hypermutants
filter(!tmb >= 10,
# There are alterations with high number of read counts.
# We will exclude those with >1000 for now.
!t_alt_count >= 1000,
!t_ref_count >= 1000) %>%
# Add `normal_cn`: Total copy number of segment in healthy tissue.
# For autosome this will be two and male sex chromosomes one.
# See: https://github.com/Roth-Lab/pyclone-vi
mutate(normal_cn = case_when(grepl("chrY", Chromosome) ~ "1",
TRUE ~ "2"))
# Calculate CCF
maf_df <- CCF(maf_df)
# Read patient list
genomic_paired_df <- readr::read_tsv(genomic_paired_file, guess_max = 100000, show_col_types = FALSE) %>%
select(!c(cancer_group, experimental_strategy)) %>%
left_join(maf_df, by = c("Kids_First_Participant_ID")) %>% # create unique identifiers
dplyr::mutate(mutation_id = paste(Kids_First_Participant_ID, tumor_descriptor, Kids_First_Biospecimen_ID, Chromosome, Start_Position, Reference_Allele, Tumor_Seq_Allele2, sep = ":"),
cg_id_kids = paste(cg_id, Kids_First_Participant_ID, sep = "_"),
cg_id_kids = str_replace(cg_id_kids, "/|-", "_"),
cg_id_kids = str_replace_all(cg_id_kids, " ", "_"),
cg_id = str_replace(cg_id, "/|-", "_"),
cg_id = str_replace_all(cg_id, " ", "_"),
sample_id = paste(tumor_descriptor, Kids_First_Biospecimen_ID, sep = ":"))
# Let's count #specimens per sample
# We will remove any samples with less than 2 specimens as phylogenies require at least 3 taxa
kids_specimens_n_df <- genomic_paired_df %>%
select(Kids_First_Participant_ID, Kids_First_Biospecimen_ID, tumor_descriptor) %>%
unique() %>%
dplyr::count(Kids_First_Participant_ID) %>%
dplyr::mutate(kids_specimens_n = glue::glue("{Kids_First_Participant_ID} (N={n})")) %>%
dplyr::rename(kids_specimens_number = n) %>%
filter(!kids_specimens_number <= 2) %>%
left_join(genomic_paired_df, by = c("Kids_First_Participant_ID")) %>%
#left_join(maf_df) %>%
filter(!is.na(Chromosome))
# Let's confirm and add the number of timepoints per sample
# In case we lost any from filtering hypermutants and high reads/alteration
timepoints_n_df <- kids_specimens_n_df %>%
select(Kids_First_Participant_ID, tumor_descriptor) %>%
unique() %>%
dplyr::count(Kids_First_Participant_ID) %>%
dplyr::mutate(kids_timepoints_n = glue::glue("{Kids_First_Participant_ID} (N={n})")) %>%
dplyr::rename(kids_timepoints_number = n) %>%
filter(!kids_timepoints_number <= 1)
df <- timepoints_n_df %>%
left_join(kids_specimens_n_df, by = c("Kids_First_Participant_ID")) %>%
write_tsv(file.path(results_dir, "samples_eligible_for_phylogeny.tsv"))
# List with samples eligible for phylogeny
# I added the information about to use for phylogenetic inferences
list_df <- df %>%
select(Kids_First_Participant_ID) %>%
unique() %>%
mutate(somatic_germline_phylogeny = case_when(grepl("PT_KZ56XHJT|PT_KTRJ8TFY", Kids_First_Participant_ID) ~ "yes",
TRUE ~ "not_yet")) %>%
write_tsv(file.path(results_dir, "samples_eligible_for_phylogeny_list.tsv"))
Add Nautilus location for Deceased specimens
nautilus_dec_df <- read_excel(nautilus_dec_file) %>%
right_join(df, by = c("sample_id", "aliquot_id", "tumor_descriptor")) %>%
write_tsv(file.path(results_dir, "nautilus_dec.tsv"))
list_df <- nautilus_dec_df %>%
select(Kids_First_Participant_ID, Kids_First_Biospecimen_ID, `Note field from Nautilus of initial parent`) %>%
unique() %>%
write_tsv(file.path(results_dir, "nautilus_dec_list.tsv"))
Other, maybe to delete
# Make list with samples with >2 biospecimens at Deceased timepoint
dec_n_df <- df %>%
filter(tumor_descriptor == "Deceased") %>%
select(Kids_First_Participant_ID, tumor_descriptor, Kids_First_Biospecimen_ID) %>%
unique() %>%
dplyr::count(Kids_First_Participant_ID) %>%
# dplyr::mutate(kids_dec_n = glue::glue("{Kids_First_Participant_ID} (N={n})")) %>%
dplyr::rename(kids_deceased_bs_number = n) %>%
filter(!kids_deceased_bs_number <= 1) %>%
write_tsv(file.path(results_dir, "kids_dec_multiple_bs_list.tsv"))
###-----------------------------------------------------
# let's look into PT_3KM9W8S8
PT_3KM9W8S8_df <- nautilus_dec_df %>%
filter(Kids_First_Participant_ID == "PT_3KM9W8S8",
Kids_First_Biospecimen_ID == "BS_2NQXY528") %>%
select(Kids_First_Participant_ID, Kids_First_Biospecimen_ID, `Note field from Nautilus of initial parent`) %>%
unique() %>%
write_tsv(file.path(results_dir, "nautilus_dec_list.tsv"))
#bs_id <- print(unique(PT_3KM9W8S8_df$Kids_First_Biospecimen_ID))
Create pyclone input files
We need to generate the input files according to the method’s
template. Phylogenetic methods require at least 2 samples per tumor site
(multiregional sampling per anatomical site). Here, we will consider
kids samples with more than 2 timepoints with one or more biospecimens.
We will compare later differences in samples with single vs multiple
biospecimens.
# Create pyclone df for all samples
pyclone_all_samples_df <- nautilus_dec_df %>%
select(Kids_First_Participant_ID, Kids_First_Biospecimen_ID, cg_id,
cg_id_kids, mutation_id, sample_id,
tumor_descriptor, Chromosome, Start_Position,
Reference_Allele, Tumor_Seq_Allele2, t_ref_count, t_alt_count,
normal_cn, tumor_fraction, mutation_count) %>%
# change names to match input requirements
dplyr::rename("ref_counts" = "t_ref_count",
"alt_counts" = "t_alt_count",
"tumour_content" = "tumor_fraction") %>%
select(Kids_First_Participant_ID, Kids_First_Biospecimen_ID, tumor_descriptor,
cg_id_kids, mutation_id, sample_id, ref_counts,
alt_counts, normal_cn, tumour_content, mutation_count)
# I will test one HGG dataset for now
# PT_Z4BF2NSB
#PT_Z4BF2NSB_DF <- pyclone_all_samples_df %>%
# filter(Kids_First_Participant_ID == "PT_Z4BF2NSB")
Add major_cn, minor_cn columns from cns files
# Create and save pyclone_input!
pyclone_input <- data_list_bind %>%
# Rename to match input format
# To figure out if the assignment is correct
dplyr::rename("major_cn" = "cn1",
"minor_cn" = "cn2") %>%
left_join(pyclone_all_samples_df) %>%
filter(!is.na(major_cn),
!is.na(minor_cn)) %>%
dplyr::mutate(mutation_id = paste(mutation_id, major_cn, minor_cn,sep = ":")) %>%
select(Kids_First_Participant_ID, tumor_descriptor, cg_id_kids, mutation_id, sample_id, ref_counts,
alt_counts, normal_cn, major_cn, minor_cn, tumour_content, mutation_count) %>%
# To ensure there are no duplicated entries in the dataframe
distinct()
Joining with `by = join_by(Kids_First_Biospecimen_ID)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.
Plot depth coverage
# Read color palette
palette_df <- readr::read_tsv(palette_file, guess_max = 100000, show_col_types = FALSE) %>%
mutate(tumor_descriptor = color_names)
# Define and order palette
palette <- palette_df$hex_codes
names(palette) <- palette_df$tumor_descriptor
# Define timepoints
timepoints = c("Diagnosis", "Progressive", "Recurrence", "Deceased", "Second Malignancy", "Unavailable")
for (i in 1:length(data_dir) ) {
# Create sample_name
sample_name <- unique(as.character(gsub(".call.cns", "", str_split_fixed(data_dir[i], "/", 13)[,11])))
sample_name <- sort(sample_name, decreasing = FALSE)
print(sample_name)
for (x in seq_along(sample_name) ) {
pyclone_input_subset <- pyclone_input %>%
filter(Kids_First_Participant_ID == sample_name[x]) %>%
select(-c(Kids_First_Participant_ID)) #%>%
# mutate(tumor_descriptor = factor(tumor_descriptor),
# tumor_descriptor = fct_relevel(tumor_descriptor, timepoints)) %>%
#arrange(tumor_descriptor, sample_id)
# Make this reproducible
set.seed(2023)
# Define label for plots
Timepoint <- factor(x = pyclone_input_subset$tumor_descriptor, levels = timepoints)
#######################
# Create bxp ref_counts
p <- print(ggplot(pyclone_input_subset, aes(sample_id, ref_counts, color = Timepoint)) +
geom_jitter(width = 0.15, size = 0.7, alpha = 0.6) +
ggplot2::geom_boxplot(color = "black",
size = 0.25,
alpha = 0,
coef = 0) + # remove whiskers
theme_Publication() +
scale_color_manual(values = palette,
breaks = sort(names(palette))) +
#rotate() +
theme(axis.text.x = element_text(angle = 90)) +
stat_summary(fun.y=mean,shape=1,col='black',geom='point') +
labs(title = sample_name[x],
x = "sample_id",
y = "ref_counts",
color = "Timepoint"))
# Save the plot
ggsave(filename = paste0(sample_name[x], "-ref_counts.pdf"),
path = pyclone_plots_dir,
width = 6,
height = 5,
device = "pdf",
useDingbats = FALSE)
#######################
# Create bxp alt_counts
p <- print(ggplot(pyclone_input_subset, aes(sample_id, alt_counts, color = Timepoint)) +
geom_jitter(width = 0.15, size = 0.7, alpha = 0.6) +
ggplot2::geom_boxplot(color = "black",
size = 0.25,
alpha = 0,
coef = 0) + # remove whiskers
theme_Publication() +
scale_color_manual(values = palette,
breaks = sort(names(palette))) +
#rotate() +
theme(axis.text.x = element_text(angle = 90)) +
stat_summary(fun.y=mean,shape=1,col='black',geom='point') +
labs(title = sample_name[x],
x = "sample_id",
y = "alt_counts",
color = "Timepoint"))
# Save the plot
ggsave(filename = paste0(sample_name[x], "-alt_counts.pdf"),
path = pyclone_plots_dir,
width = 6,
height = 5,
device = "pdf",
useDingbats = FALSE)
}
}
[1] "PT_KTRJ8TFY"
[1] "PT_KTRJ8TFY"
[1] "PT_KTRJ8TFY"
[1] "PT_KTRJ8TFY"
[1] "PT_KTRJ8TFY"
[1] "PT_KTRJ8TFY"
[1] "PT_KTRJ8TFY"
[1] "PT_KTRJ8TFY"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_Z4BF2NSB"
[1] "PT_Z4BF2NSB"
[1] "PT_Z4BF2NSB"
[1] "PT_Z4BF2NSB"
[1] "PT_Z4BF2NSB"
[1] "PT_Z4BF2NSB"
[1] "PT_Z4BF2NSB"
[1] "PT_Z4BF2NSB"
























































Total number of mutations across timepoints and biospecimen sample
per Patient case
for (i in 1:length(data_dir) ) {
# Create sample_name
sample_name <- unique(as.character(gsub(".call.cns", "", str_split_fixed(data_dir[i], "/", 13)[,11])))
sample_name <- sort(sample_name, decreasing = FALSE)
print(sample_name)
for (x in seq_along(sample_name) ) {
pyclone_input_subset <- pyclone_input %>%
filter(Kids_First_Participant_ID == sample_name[x]) %>%
select(-c(Kids_First_Participant_ID))
# Define parameters for function
ylim = max(pyclone_input_subset$mutation_count)
# Rename legend for timepoints
Timepoint <- factor(pyclone_input_subset$tumor_descriptor)
# Plot stacked barplot
print(ggplot(pyclone_input_subset, aes(x = sample_id,
y = mutation_count,
fill = Timepoint)) +
geom_col(position = position_stack(reverse = TRUE)) +
geom_bar(stat = "identity", width = 0.5) +
scale_fill_manual(values = palette, breaks = sort(names(palette))) +
theme_Publication() +
theme(axis.text.x = element_text(angle = 85,
hjust = 1,
vjust = 1)) +
labs(title = paste(sample_name)) +
labs(x = "sample_id", y = "Total Mutations") +
ylim(0, ylim))
}
}
[1] "PT_KTRJ8TFY"
[1] "PT_KTRJ8TFY"
[1] "PT_KTRJ8TFY"
[1] "PT_KTRJ8TFY"
[1] "PT_KTRJ8TFY"
[1] "PT_KTRJ8TFY"
[1] "PT_KTRJ8TFY"
[1] "PT_KTRJ8TFY"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_Z4BF2NSB"
[1] "PT_Z4BF2NSB"
[1] "PT_Z4BF2NSB"
[1] "PT_Z4BF2NSB"
[1] "PT_Z4BF2NSB"
[1] "PT_Z4BF2NSB"
[1] "PT_Z4BF2NSB"
[1] "PT_Z4BF2NSB"




























sessionInfo()
R version 4.3.1 (2023-06-16)
Platform: x86_64-apple-darwin20 (64-bit)
Running under: macOS Ventura 13.6.1
Matrix products: default
BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.3-x86_64/Resources/lib/libRlapack.dylib; LAPACK version 3.11.0
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
time zone: America/New_York
tzcode source: internal
attached base packages:
[1] grid stats graphics grDevices utils datasets methods base
other attached packages:
[1] ggthemes_4.2.4 cDriver_0.4.2 readxl_1.4.3 gridExtra_2.3 clonevol_0.99.11 lubridate_1.9.3 forcats_1.0.0 stringr_1.5.1 dplyr_1.1.4
[10] purrr_1.0.2 readr_2.1.4 tidyr_1.3.0 tibble_3.2.1 ggplot2_3.4.4 tidyverse_2.0.0 dndscv_0.0.1.0
loaded via a namespace (and not attached):
[1] ade4_1.7-22 tidyselect_1.2.0 Rmpfr_0.9-3 farver_2.1.1 Biostrings_2.70.1 bitops_1.0-7
[7] fastmap_1.1.1 RCurl_1.98-1.13 digest_0.6.33 timechange_0.2.0 lifecycle_1.0.4 magrittr_2.0.3
[13] compiler_4.3.1 rlang_1.1.2 sass_0.4.7 tools_4.3.1 utf8_1.2.4 yaml_2.3.7
[19] data.table_1.14.8 knitr_1.45 labeling_0.4.3 bit_4.0.5 abind_1.4-5 BiocParallel_1.36.0
[25] withr_2.5.2 BiocGenerics_0.48.1 stats4_4.3.1 fansi_1.0.5 colorspace_2.1-0 scales_1.2.1
[31] MASS_7.3-60 cli_3.6.1 rmarkdown_2.25 crayon_1.5.2 ragg_1.2.6 generics_0.1.3
[37] rstudioapi_0.15.0 tzdb_0.4.0 cachem_1.0.8 zlibbioc_1.48.0 parallel_4.3.1 cellranger_1.1.0
[43] XVector_0.42.0 vctrs_0.6.4 jsonlite_1.8.7 carData_3.0-5 car_3.1-2 IRanges_2.36.0
[49] hms_1.1.3 S4Vectors_0.40.1 bit64_4.0.5 rstatix_0.7.2 seqinr_4.2-30 systemfonts_1.0.5
[55] jquerylib_0.1.4 glue_1.6.2 codetools_0.2-19 stringi_1.8.1 gtable_0.3.4 GenomeInfoDb_1.38.1
[61] GenomicRanges_1.54.1 gmp_0.7-2 munsell_0.5.0 pillar_1.9.0 htmltools_0.5.7 GenomeInfoDbData_1.2.11
[67] R6_2.5.1 textshaping_0.3.7 rprojroot_2.0.4 vroom_1.6.4 evaluate_0.23 backports_1.4.1
[73] Rsamtools_2.18.0 broom_1.0.5 bslib_0.5.1 Rcpp_1.0.11 xfun_0.41 pkgconfig_2.0.3
LS0tCnRpdGxlOiAiSW5mZXJlbmNlIG9mIHN1YmNsb25hbCBhcmNoaXRlY3R1cmUgb2YgdHVtb3JzIGFjcm9zcyBtdWx0aXBsZSB0aW1lcG9pbnRzIGluIHRoZSBwYWlyZWQgbG9uZ2l0dWRpbmFsIChQTCkgY29ob3J0IgphdXRob3I6ICdBbnRvbmlhIENocm9uaSA8Y2hyb25pYUBjaG9wLmVkdT4gZm9yIEQzQicKZGF0ZTogIjIwMjMiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiBUUlVFCiAgICB0b2NfZmxvYXQ6IFRSVUUKLS0tCgojIEJhY2tncm91bmQKClRoaXMgbm90ZWJvb2sgaXMgZm9yIHByZXBhcmluZyBpbnB1dCBmaWxlcyBmb3IgcHljbG9uZS12aSAoaHR0cHM6Ly9naXRodWIuY29tL1JvdGgtTGFiL3B5Y2xvbmUtdmkpLiBUaGUgc2FtZSBmaWxlcyB3aWxsIGJlIHVzZWQgYXMgYW4gaW5wdXQgZm9yIEZhc3RDbG9uZSAoaHR0cHM6Ly9naXRodWIuY29tL0d1YW5MYWIvRmFzdENsb25lX0d1YW5MYWIpLgoKCiMgU2V0IHVwCgpgYGB7ciBsb2FkLWxpYnJhcnl9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CiAgbGlicmFyeSh0aWR5dmVyc2UpCiAgbGlicmFyeShyZWFkeGwpCiAgbGlicmFyeShjRHJpdmVyKSAjIENhbGN1bGF0ZSBDQ0YsIGh0dHBzOi8vZ2l0aHViLmNvbS9oYW5hc3VzYWsvY0RyaXZlci8KfSkKYGBgCgojIERpcmVjdG9yaWVzIGFuZCBGaWxlIElucHV0cy9PdXRwdXRzCgpgYGB7ciBzZXQtZGlyLWFuZC1maWxlLW5hbWVzfQojIERldGVjdCB0aGUgIi5naXQiIGZvbGRlciAtLSB0aGlzIHdpbGwgYmUgaW4gdGhlIHByb2plY3Qgcm9vdCBkaXJlY3RvcnkKIyBVc2UgdGhpcyBhcyB0aGUgcm9vdCBkaXJlY3RvcnkgdG8gZW5zdXJlIHByb3BlciBzb3VyY2luZyBvZiBmdW5jdGlvbnMgCiMgbm8gbWF0dGVyIHdoZXJlIHRoaXMgaXMgY2FsbGVkIGZyb20Kcm9vdF9kaXIgPC0gcnByb2pyb290OjpmaW5kX3Jvb3QocnByb2pyb290OjpoYXNfZGlyKCIuZ2l0IikpCmFuYWx5c2lzX2RpciA8LSBmaWxlLnBhdGgocm9vdF9kaXIsICJhbmFseXNlcyIsICJ0dW1vci1jbG9uZS1pbmZlcmVuY2UiKQppbnB1dF9kaXIgPC0gZmlsZS5wYXRoKGFuYWx5c2lzX2RpciwgImlucHV0IikKZGF0YV9kaXIgPC0gZmlsZS5wYXRoKHJvb3RfZGlyLCAiZGF0YSIpCmZpbGVzX2RpciA8LSBmaWxlLnBhdGgocm9vdF9kaXIsICJhbmFseXNlcyIsICJzYW1wbGUtZGlzdHJpYnV0aW9uLWFuYWx5c2lzIiwgInJlc3VsdHMiKQptYWZfZmlsZXNfZGlyIDwtIGZpbGUucGF0aChyb290X2RpciwgImFuYWx5c2VzIiwgInRtYi12YWYtbG9uZ2l0dWRpbmFsIiwgInJlc3VsdHMiKQoKIyBJbnB1dCBmaWxlcwojcGJ0YV9maWxlIDwtIGZpbGUucGF0aChmaWxlc19kaXIsICJwYnRhLnRzdiIpICMgZmlsZSBmcm9tIGFkZC1zYW1wbGUtZGlzdHJpYnV0aW9uIG1vZHVsZQptYWZfZmlsZSA8LSBmaWxlLnBhdGgobWFmX2ZpbGVzX2RpciwgInRtYl92YWZfZ2Vub21pYy50c3YiKQpnZW5vbWljX3BhaXJlZF9maWxlIDwtIGZpbGUucGF0aChmaWxlc19kaXIsICJnZW5vbWljX2Fzc2F5c19tYXRjaGVkX3RpbWVfcG9pbnRzLnRzdiIpICMgZmlsZSBmcm9tIGFkZC1zYW1wbGUtZGlzdHJpYnV0aW9uIG1vZHVsZQpuYXV0aWx1c19kZWNfZmlsZSA8LSBmaWxlLnBhdGgoaW5wdXRfZGlyLCAiZGVjZWFzZWRfc2FtcGxlcy54bHN4IikgCnBhbGV0dGVfZmlsZSA8LSBmaWxlLnBhdGgocm9vdF9kaXIsICJmaWd1cmVzIiwgInBhbGV0dGVzIiwgInR1bW9yX2Rlc2NyaXB0b3JfY29sb3JfcGFsZXR0ZS50c3YiKQoKCiMgRVhBTVBMRSAKIyBCdXQgdG8gcmVwbGFjZSB3aXRoIGRpciB3aXRoIGNucyBkYXRhIGluZmVycmVkIGJ5IENOVmtpdAojIFBUX1o0QkYyTlNCX2RpciA8LSBmaWxlLnBhdGgoaW5wdXRfZGlyLCAiY252a2l0X2RhdGFfZXhhbXBsZS9QVF9aNEJGMk5TQiIpIApjbnNfZGlyIDwtIGZpbGUucGF0aChpbnB1dF9kaXIsICJjbnZraXRfZGF0YSIpIAoKIyBGaWxlIHBhdGggdG8gcmVzdWx0cyBkaXJlY3RvcnkKcmVzdWx0c19kaXIgPC0KICBmaWxlLnBhdGgoYW5hbHlzaXNfZGlyLCAicmVzdWx0cyIpCmlmICghZGlyLmV4aXN0cyhyZXN1bHRzX2RpcikpIHsKICBkaXIuY3JlYXRlKHJlc3VsdHNfZGlyKQp9CgojIEZpbGUgcGF0aCB0byBpbnB1dCBkaXJlY3RvcnkKcHljbG9uZXZpX2lucHV0X2RpciA8LQogIGZpbGUucGF0aChhbmFseXNpc19kaXIsICJyZXN1bHRzIiwgInB5Y2xvbmUtdmktaW5wdXQiKQppZiAoIWRpci5leGlzdHMocHljbG9uZXZpX2lucHV0X2RpcikpIHsKICBkaXIuY3JlYXRlKHB5Y2xvbmV2aV9pbnB1dF9kaXIpCn0KCiMgRmlsZSBwYXRoIHRvIGlucHV0IGRpcmVjdG9yeQpmYXN0Y2xvbmVfaW5wdXRfZGlyIDwtCiAgZmlsZS5wYXRoKGFuYWx5c2lzX2RpciwgInJlc3VsdHMiLCAiZmFzdGNsb25lLWlucHV0IikKaWYgKCFkaXIuZXhpc3RzKHB5Y2xvbmV2aV9pbnB1dF9kaXIpKSB7CiAgZGlyLmNyZWF0ZShweWNsb25ldmlfaW5wdXRfZGlyKQp9CgoKIyBGaWxlIHBhdGggdG8gcGxvdCBkaXJlY3RvcnkKcHljbG9uZV9wbG90c19kaXIgPC0KICBmaWxlLnBhdGgoYW5hbHlzaXNfZGlyLCAicGxvdHMiLCAicHljbG9uZS12aSIpCmlmICghZGlyLmV4aXN0cyhweWNsb25lX3Bsb3RzX2RpcikpIHsKICBkaXIuY3JlYXRlKHB5Y2xvbmVfcGxvdHNfZGlyKQp9CgoKc291cmNlKHBhc3RlMChyb290X2RpciwgIi9maWd1cmVzL3NjcmlwdHMvdGhlbWUuUiIpKQpgYGAKCiMgTG9hZCBhbmQgcHJvY2VzcyBkYXRhCgpgYGB7ciBsb2FkLXByb2Nlc3MtaW5wdXRzfQojIFJlYWQgbWFmCm1hZl9kZiA8LSByZWFkcjo6cmVhZF90c3YobWFmX2ZpbGUsIGd1ZXNzX21heCA9IDEwMDAwMCwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkgJT4lCiAgc2VsZWN0KEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQsIEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQsIGNnX2lkLCB0dW1vcl9kZXNjcmlwdG9yLAogICAgICAgICBzYW1wbGVfaWQsIGFsaXF1b3RfaWQsIENocm9tb3NvbWUsIAogICAgICAgICBTdGFydF9Qb3NpdGlvbiwgUmVmZXJlbmNlX0FsbGVsZSwgVHVtb3JfU2VxX0FsbGVsZTEsIFR1bW9yX1NlcV9BbGxlbGUyLAogICAgICAgICB0X3JlZl9jb3VudCwgdF9hbHRfY291bnQsIHRtYiwgCiAgICAgICAgIHR1bW9yX2ZyYWN0aW9uLCB0dW1vcl9wbG9pZHksIFZBRiwgSHVnb19TeW1ib2wsCiAgICAgICAgIG11dGF0aW9uX2NvdW50KSAlPiUgCgogICMgUmVtb3ZlIGh5cGVybXV0YW50cwogIGZpbHRlcighdG1iID49IDEwLCAKICAKICAjIFRoZXJlIGFyZSBhbHRlcmF0aW9ucyB3aXRoIGhpZ2ggbnVtYmVyIG9mIHJlYWQgY291bnRzLgogICMgV2Ugd2lsbCBleGNsdWRlIHRob3NlIHdpdGggPjEwMDAgZm9yIG5vdy4KICAgICAgICAgIXRfYWx0X2NvdW50ID49IDEwMDAsCiAgICAgICAgICF0X3JlZl9jb3VudCA+PSAxMDAwKSAgJT4lIAogIAogICMgQWRkIGBub3JtYWxfY25gOiBUb3RhbCBjb3B5IG51bWJlciBvZiBzZWdtZW50IGluIGhlYWx0aHkgdGlzc3VlLiAKICAjIEZvciBhdXRvc29tZSB0aGlzIHdpbGwgYmUgdHdvIGFuZCBtYWxlIHNleCBjaHJvbW9zb21lcyBvbmUuCiAgIyBTZWU6IGh0dHBzOi8vZ2l0aHViLmNvbS9Sb3RoLUxhYi9weWNsb25lLXZpCiAgbXV0YXRlKG5vcm1hbF9jbiA9IGNhc2Vfd2hlbihncmVwbCgiY2hyWSIsIENocm9tb3NvbWUpIH4gIjEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAiMiIpKQojIENhbGN1bGF0ZSBDQ0YKbWFmX2RmIDwtIENDRihtYWZfZGYpCiAKIyBSZWFkIHBhdGllbnQgbGlzdApnZW5vbWljX3BhaXJlZF9kZiA8LSByZWFkcjo6cmVhZF90c3YoZ2Vub21pY19wYWlyZWRfZmlsZSwgZ3Vlc3NfbWF4ID0gMTAwMDAwLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKSAlPiUgCiAgc2VsZWN0KCFjKGNhbmNlcl9ncm91cCwgZXhwZXJpbWVudGFsX3N0cmF0ZWd5KSkgJT4lIAogIGxlZnRfam9pbihtYWZfZGYsIGJ5ID0gYygiS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCIpKSAlPiUgIyBjcmVhdGUgdW5pcXVlIGlkZW50aWZpZXJzCiAgZHBseXI6Om11dGF0ZShtdXRhdGlvbl9pZCA9IHBhc3RlKEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQsIHR1bW9yX2Rlc2NyaXB0b3IsIEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQsIENocm9tb3NvbWUsIFN0YXJ0X1Bvc2l0aW9uLCBSZWZlcmVuY2VfQWxsZWxlLCBUdW1vcl9TZXFfQWxsZWxlMiwgc2VwID0gIjoiKSwKICAgICAgICAgICAgICAgIGNnX2lkX2tpZHMgPSBwYXN0ZShjZ19pZCwgS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCwgc2VwID0gIl8iKSwKICAgICAgICAgICAgICAgIGNnX2lkX2tpZHMgPSBzdHJfcmVwbGFjZShjZ19pZF9raWRzLCAiL3wtIiwgIl8iKSwKICAgICAgICAgICAgICAgIGNnX2lkX2tpZHMgPSBzdHJfcmVwbGFjZV9hbGwoY2dfaWRfa2lkcywgIiAiLCAiXyIpLAogICAgICAgICAgICAgICAgY2dfaWQgPSBzdHJfcmVwbGFjZShjZ19pZCwgIi98LSIsICJfIiksCiAgICAgICAgICAgICAgICBjZ19pZCA9IHN0cl9yZXBsYWNlX2FsbChjZ19pZCwgIiAiLCAiXyIpLAogICAgICAgICAgICAgICAgc2FtcGxlX2lkID0gcGFzdGUodHVtb3JfZGVzY3JpcHRvciwgS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCwgc2VwID0gIjoiKSkKCgojIExldCdzIGNvdW50ICNzcGVjaW1lbnMgcGVyIHNhbXBsZSAKIyBXZSB3aWxsIHJlbW92ZSBhbnkgc2FtcGxlcyB3aXRoIGxlc3MgdGhhbiAyIHNwZWNpbWVucyBhcyBwaHlsb2dlbmllcyByZXF1aXJlIGF0IGxlYXN0IDMgdGF4YQpraWRzX3NwZWNpbWVuc19uX2RmIDwtIGdlbm9taWNfcGFpcmVkX2RmICU+JSAKICBzZWxlY3QoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCwgS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCwgdHVtb3JfZGVzY3JpcHRvcikgJT4lIAogIHVuaXF1ZSgpICU+JSAKICBkcGx5cjo6Y291bnQoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCkgJT4lIAogIGRwbHlyOjptdXRhdGUoa2lkc19zcGVjaW1lbnNfbiA9IGdsdWU6OmdsdWUoIntLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEfSAgKE49e259KSIpKSAlPiUKICBkcGx5cjo6cmVuYW1lKGtpZHNfc3BlY2ltZW5zX251bWJlciA9IG4pICU+JSAKICBmaWx0ZXIoIWtpZHNfc3BlY2ltZW5zX251bWJlciA8PSAyKSAlPiUKCiAgbGVmdF9qb2luKGdlbm9taWNfcGFpcmVkX2RmLCBieSA9IGMoIktpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQiKSkgJT4lCiAgI2xlZnRfam9pbihtYWZfZGYpICU+JSAKICBmaWx0ZXIoIWlzLm5hKENocm9tb3NvbWUpKSAKICAKIyBMZXQncyBjb25maXJtIGFuZCBhZGQgdGhlIG51bWJlciBvZiB0aW1lcG9pbnRzIHBlciBzYW1wbGUKIyBJbiBjYXNlIHdlIGxvc3QgYW55IGZyb20gZmlsdGVyaW5nIGh5cGVybXV0YW50cyBhbmQgaGlnaCByZWFkcy9hbHRlcmF0aW9uCnRpbWVwb2ludHNfbl9kZiA8LSBraWRzX3NwZWNpbWVuc19uX2RmICU+JSAKICBzZWxlY3QoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCwgdHVtb3JfZGVzY3JpcHRvcikgJT4lIAogIHVuaXF1ZSgpICU+JSAKICBkcGx5cjo6Y291bnQoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCkgJT4lIAogIGRwbHlyOjptdXRhdGUoa2lkc190aW1lcG9pbnRzX24gPSBnbHVlOjpnbHVlKCJ7S2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRH0gIChOPXtufSkiKSkgJT4lCiAgZHBseXI6OnJlbmFtZShraWRzX3RpbWVwb2ludHNfbnVtYmVyID0gbikgJT4lIAogIGZpbHRlcigha2lkc190aW1lcG9pbnRzX251bWJlciA8PSAxKQoKZGYgPC0gdGltZXBvaW50c19uX2RmICU+JSAKICBsZWZ0X2pvaW4oa2lkc19zcGVjaW1lbnNfbl9kZiwgYnkgPSBjKCJLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEIikpICU+JSAKICB3cml0ZV90c3YoZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAic2FtcGxlc19lbGlnaWJsZV9mb3JfcGh5bG9nZW55LnRzdiIpKQoKIyBMaXN0IHdpdGggc2FtcGxlcyBlbGlnaWJsZSBmb3IgcGh5bG9nZW55CiMgSSBhZGRlZCB0aGUgaW5mb3JtYXRpb24gYWJvdXQgdG8gdXNlIGZvciBwaHlsb2dlbmV0aWMgaW5mZXJlbmNlcwpsaXN0X2RmIDwtIGRmICU+JSAKICBzZWxlY3QoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCkgJT4lIAogIHVuaXF1ZSgpICU+JSAKICBtdXRhdGUoc29tYXRpY19nZXJtbGluZV9waHlsb2dlbnkgPSBjYXNlX3doZW4oZ3JlcGwoIlBUX0taNTZYSEpUfFBUX0tUUko4VEZZIiwgS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCkgfiAieWVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAibm90X3lldCIpKSAlPiUgCiAgd3JpdGVfdHN2KGZpbGUucGF0aChyZXN1bHRzX2RpciwgInNhbXBsZXNfZWxpZ2libGVfZm9yX3BoeWxvZ2VueV9saXN0LnRzdiIpKQoKYGBgCgojIyBBZGQgTmF1dGlsdXMgbG9jYXRpb24gZm9yIERlY2Vhc2VkIHNwZWNpbWVucwoKYGBge3IgYWRkLU5hdXRpbHVzLWxvY2F0aW9ufQpuYXV0aWx1c19kZWNfZGYgPC0gcmVhZF9leGNlbChuYXV0aWx1c19kZWNfZmlsZSkgJT4lIAogIHJpZ2h0X2pvaW4oZGYsIGJ5ID0gYygic2FtcGxlX2lkIiwgImFsaXF1b3RfaWQiLCAidHVtb3JfZGVzY3JpcHRvciIpKSAlPiUgCiAgd3JpdGVfdHN2KGZpbGUucGF0aChyZXN1bHRzX2RpciwgIm5hdXRpbHVzX2RlYy50c3YiKSkKCmxpc3RfZGYgPC0gbmF1dGlsdXNfZGVjX2RmICU+JSAKICBzZWxlY3QoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCwgS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCwgYE5vdGUgZmllbGQgZnJvbSBOYXV0aWx1cyBvZiBpbml0aWFsIHBhcmVudGApICU+JSAKICB1bmlxdWUoKSAlPiUgCiAgd3JpdGVfdHN2KGZpbGUucGF0aChyZXN1bHRzX2RpciwgIm5hdXRpbHVzX2RlY19saXN0LnRzdiIpKQoKYGBgCgojIyBPdGhlciwgbWF5YmUgdG8gZGVsZXRlCgpgYGAge3Igb3RoZXItdGhpbmdzfQoKIyBNYWtlIGxpc3Qgd2l0aCBzYW1wbGVzIHdpdGggPjIgYmlvc3BlY2ltZW5zIGF0IERlY2Vhc2VkIHRpbWVwb2ludApkZWNfbl9kZiA8LSBkZiAlPiUgCiAgZmlsdGVyKHR1bW9yX2Rlc2NyaXB0b3IgPT0gIkRlY2Vhc2VkIikgJT4lIAogIHNlbGVjdChLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lELCB0dW1vcl9kZXNjcmlwdG9yLCBLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEKSAlPiUgCiAgdW5pcXVlKCkgJT4lIAogIGRwbHlyOjpjb3VudChLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEKSAlPiUgCiAjIGRwbHlyOjptdXRhdGUoa2lkc19kZWNfbiA9IGdsdWU6OmdsdWUoIntLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEfSAgKE49e259KSIpKSAlPiUKICBkcGx5cjo6cmVuYW1lKGtpZHNfZGVjZWFzZWRfYnNfbnVtYmVyID0gbikgJT4lIAogIGZpbHRlcigha2lkc19kZWNlYXNlZF9ic19udW1iZXIgPD0gMSkgJT4lIAogIHdyaXRlX3RzdihmaWxlLnBhdGgocmVzdWx0c19kaXIsICJraWRzX2RlY19tdWx0aXBsZV9ic19saXN0LnRzdiIpKQoKIyMjLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBsZXQncyBsb29rIGludG8gUFRfM0tNOVc4UzgKUFRfM0tNOVc4UzhfZGYgPC0gbmF1dGlsdXNfZGVjX2RmICU+JSAKICBmaWx0ZXIoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCA9PSAiUFRfM0tNOVc4UzgiLAogICAgICAgICBLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEID09ICJCU18yTlFYWTUyOCIpICU+JSAKICBzZWxlY3QoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCwgS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCwgYE5vdGUgZmllbGQgZnJvbSBOYXV0aWx1cyBvZiBpbml0aWFsIHBhcmVudGApICU+JSAKICB1bmlxdWUoKSAlPiUgCiAgd3JpdGVfdHN2KGZpbGUucGF0aChyZXN1bHRzX2RpciwgIm5hdXRpbHVzX2RlY19saXN0LnRzdiIpKQogIAojYnNfaWQgPC0gcHJpbnQodW5pcXVlKFBUXzNLTTlXOFM4X2RmJEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQpKQpgYGAKCgojIENyZWF0ZSBweWNsb25lIGlucHV0IGZpbGVzIAoKV2UgbmVlZCB0byBnZW5lcmF0ZSB0aGUgaW5wdXQgZmlsZXMgYWNjb3JkaW5nIHRvIHRoZSBtZXRob2QncyB0ZW1wbGF0ZS4gClBoeWxvZ2VuZXRpYyBtZXRob2RzIHJlcXVpcmUgYXQgbGVhc3QgMiBzYW1wbGVzIHBlciB0dW1vciBzaXRlIChtdWx0aXJlZ2lvbmFsIHNhbXBsaW5nIHBlciBhbmF0b21pY2FsIHNpdGUpLgpIZXJlLCB3ZSB3aWxsIGNvbnNpZGVyIGtpZHMgc2FtcGxlcyB3aXRoIG1vcmUgdGhhbiAyIHRpbWVwb2ludHMgd2l0aCBvbmUgb3IgbW9yZSBiaW9zcGVjaW1lbnMuIApXZSB3aWxsIGNvbXBhcmUgbGF0ZXIgZGlmZmVyZW5jZXMgaW4gc2FtcGxlcyB3aXRoIHNpbmdsZSB2cyBtdWx0aXBsZSBiaW9zcGVjaW1lbnMuCgpgYGB7ciBjcmVhdGUtcHljbG9uZS1hbGwtc2FtcGxlcy1kZn0KIyBDcmVhdGUgcHljbG9uZSBkZiBmb3IgYWxsIHNhbXBsZXMKcHljbG9uZV9hbGxfc2FtcGxlc19kZiA8LSBuYXV0aWx1c19kZWNfZGYgJT4lCiAgc2VsZWN0KEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQsIEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQsIGNnX2lkLCAKICAgICAgICAgY2dfaWRfa2lkcywgbXV0YXRpb25faWQsIHNhbXBsZV9pZCwKICAgICAgICAgdHVtb3JfZGVzY3JpcHRvciwgQ2hyb21vc29tZSwgU3RhcnRfUG9zaXRpb24sIAogICAgICAgICBSZWZlcmVuY2VfQWxsZWxlLCBUdW1vcl9TZXFfQWxsZWxlMiwgdF9yZWZfY291bnQsIHRfYWx0X2NvdW50LAogICAgICAgICBub3JtYWxfY24sIHR1bW9yX2ZyYWN0aW9uLCBtdXRhdGlvbl9jb3VudCkgJT4lIAogIAogICMgY2hhbmdlIG5hbWVzIHRvIG1hdGNoIGlucHV0IHJlcXVpcmVtZW50cwogIGRwbHlyOjpyZW5hbWUoInJlZl9jb3VudHMiID0gInRfcmVmX2NvdW50IiwgCiAgICAgICAgICAgICAgICAiYWx0X2NvdW50cyIgPSAidF9hbHRfY291bnQiLAogICAgICAgICAgICAgICAgInR1bW91cl9jb250ZW50IiA9ICJ0dW1vcl9mcmFjdGlvbiIpICU+JQogIHNlbGVjdChLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lELCBLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lELCB0dW1vcl9kZXNjcmlwdG9yLCAKICAgICAgICAgY2dfaWRfa2lkcywgbXV0YXRpb25faWQsIHNhbXBsZV9pZCwgcmVmX2NvdW50cywgCiAgICAgICAgIGFsdF9jb3VudHMsIG5vcm1hbF9jbiwgdHVtb3VyX2NvbnRlbnQsIG11dGF0aW9uX2NvdW50KQogCgojIEkgd2lsbCB0ZXN0IG9uZSBIR0cgZGF0YXNldCBmb3Igbm93CiMgUFRfWjRCRjJOU0IKCiNQVF9aNEJGMk5TQl9ERiA8LSBweWNsb25lX2FsbF9zYW1wbGVzX2RmICU+JSAKIyAgZmlsdGVyKEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQgPT0gIlBUX1o0QkYyTlNCIikKICAgICAgICAgICAgICAgICAgIApgYGAKCiMjIEFkZCBtYWpvcl9jbiwgbWlub3JfY24gY29sdW1ucyBmcm9tIGNucyBmaWxlcwoKYGBge3IgcHJvY2Vzcy1jbnMtZmlsZXN9CmRhdGFfZGlyIDwtIGRpcihwYXRoID0gY25zX2RpciwgIHBhdHRlcm4gPSAiLmNhbGwuY25zIiwgZnVsbC5uYW1lcyA9IFRSVUUsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIENyZWF0ZSBsaXN0IApkYXRhX2xpc3QgPC0gbGlzdCgpCgpmb3IgKGkgaW4gMTpsZW5ndGgoZGF0YV9kaXIpICkgeyAKICAKICAjIENyZWF0ZSBzYW1wbGVfbmFtZQogIHNhbXBsZV9uYW1lIDwtICB1bmlxdWUoYXMuY2hhcmFjdGVyKGdzdWIoIi5jYWxsLmNucyIsICIiLCBzdHJfc3BsaXRfZml4ZWQoZGF0YV9kaXJbaV0sICIvIiwgMTMpWywxMV0pKSkKICBzYW1wbGVfbmFtZSA8LSBzb3J0KHNhbXBsZV9uYW1lLCBkZWNyZWFzaW5nID0gRkFMU0UpCiAgcHJpbnQoc2FtcGxlX25hbWUpCiAgCiAgZm9yICh4IGluIHNlcV9hbG9uZyhzYW1wbGVfbmFtZSkgKSB7IAogICAgCiAgICBkYXRhX2xpc3RbW2ldXSA8LSByZWFkLmNzdihkYXRhX2RpcltpXSwgaGVhZGVyPVQsIHNlcD0iXHQiKSAKICAKICAgIAogICMgQ3JlYXRlIGZpbGVfbmFtZQogIGZpbGVfbmFtZSA8LSBnc3ViKCIuY2FsbC5jbnMiLCAiIiwgc3RyX3NwbGl0X2ZpeGVkKGRhdGFfZGlyW2ldLCAiLyIsIDEzKVssMTJdKQogIHByaW50KGZpbGVfbmFtZSkKICAKICBkYXRhX2xpc3RbW2ldXSA8LSBkYXRhX2xpc3RbW2ldXSAlPiUgCiAgICBtdXRhdGUoS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCA9IGZpbGVfbmFtZSkKICAKICAjIFRoZSBmb2xsb3dpbmcgY29kZSBhc3NpZ25zIG5hbWUgdG8gZGYKICAjIEJ1dCBhZGRzIGFuIGV4dHJhIGRmIGludG8gdGhlIGxpc3QgLSBkb250IGtub3cgeWV0IHdoeSBidXQgbGV0J3MgcmVtb3ZlLCBpdCdzIG5vdCBuZWNlc3NhcnkKICAjZGYgPC0gYXNzaWduKGZpbGVfbmFtZSwgZGF0YV9saXN0KSAKICAjZGF0YV9saXN0W1tmaWxlX25hbWVdXSA8LSBkZgogIH0KfQoKIyBCaW5kIGFsbCBkZiBmcm9tIGxpc3QgCmRhdGFfbGlzdF9iaW5kIDwtIGRwbHlyOjpiaW5kX3Jvd3MoZGF0YV9saXN0KSAKCiMgQ3JlYXRlIGFuZCBzYXZlIHB5Y2xvbmVfaW5wdXQhCnB5Y2xvbmVfaW5wdXQgPC0gZGF0YV9saXN0X2JpbmQgJT4lIAogIAogICMgUmVuYW1lIHRvIG1hdGNoIGlucHV0IGZvcm1hdAogICMgVG8gZmlndXJlIG91dCBpZiB0aGUgYXNzaWdubWVudCBpcyBjb3JyZWN0CiAgZHBseXI6OnJlbmFtZSgibWFqb3JfY24iID0gImNuMSIsIAogICAgICAgICAgICAgICAgIm1pbm9yX2NuIiA9ICJjbjIiKSAlPiUgCgogIGxlZnRfam9pbihweWNsb25lX2FsbF9zYW1wbGVzX2RmKSAlPiUgCiAgZmlsdGVyKCFpcy5uYShtYWpvcl9jbiksCiAgICAgICAgICFpcy5uYShtaW5vcl9jbikpICU+JQogIGRwbHlyOjptdXRhdGUobXV0YXRpb25faWQgPSBwYXN0ZShtdXRhdGlvbl9pZCwgbWFqb3JfY24sIG1pbm9yX2NuLHNlcCA9ICI6IikpICU+JSAKICBzZWxlY3QoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCwgdHVtb3JfZGVzY3JpcHRvciwgY2dfaWRfa2lkcywgbXV0YXRpb25faWQsIHNhbXBsZV9pZCwgcmVmX2NvdW50cywgCiAgICAgICAgIGFsdF9jb3VudHMsIG5vcm1hbF9jbiwgbWFqb3JfY24sIG1pbm9yX2NuLCB0dW1vdXJfY29udGVudCwgbXV0YXRpb25fY291bnQpICU+JSAKICAKICAjIFRvIGVuc3VyZSB0aGVyZSBhcmUgbm8gZHVwbGljYXRlZCBlbnRyaWVzIGluIHRoZSBkYXRhZnJhbWUKICBkaXN0aW5jdCgpIAoKCmZvciAoaSBpbiAxOmxlbmd0aChkYXRhX2RpcikgKSB7IAogIAogICMgQ3JlYXRlIHNhbXBsZV9uYW1lCiAgc2FtcGxlX25hbWUgPC0gIHVuaXF1ZShhcy5jaGFyYWN0ZXIoZ3N1YigiLmNhbGwuY25zIiwgIiIsIHN0cl9zcGxpdF9maXhlZChkYXRhX2RpcltpXSwgIi8iLCAxMylbLDExXSkpKQogIHNhbXBsZV9uYW1lIDwtIHNvcnQoc2FtcGxlX25hbWUsIGRlY3JlYXNpbmcgPSBGQUxTRSkKICBwcmludChzYW1wbGVfbmFtZSkKICAKZm9yICh4IGluIHNlcV9hbG9uZyhzYW1wbGVfbmFtZSkgKSB7IAogIAogICMgU2F2ZSBpbnB1dCBmaWxlIGZvciBweWNsb25lLXZpCiAgZm5hbWUgPC0gcGFzdGUwKHB5Y2xvbmV2aV9pbnB1dF9kaXIsICIvIiwgc2FtcGxlX25hbWVbeF0sICIudHN2IikKICBwcmludChmbmFtZSkKICAKICBweWNsb25lX2lucHV0X3N1YnNldCA8LSBweWNsb25lX2lucHV0ICU+JQogICAgZmlsdGVyKEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQgPT0gc2FtcGxlX25hbWVbeF0pICU+JSAKICAgICNzZWxlY3QoLWMoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCkpICU+JSAKICB3cml0ZV90c3YoZmlsZS5wYXRoKGZuYW1lKSkKICAKICAKICAjIFNhdmUgaW5wdXQgZmlsZSBmb3IgRmFzdENsb25lCiAgZmFzdGNsb25lX2ZuYW1lIDwtIHBhc3RlMChmYXN0Y2xvbmVfaW5wdXRfZGlyLCAiLyIsIHNhbXBsZV9uYW1lW3hdLCAiLnRzdiIpCiAgcHJpbnQoZmFzdGNsb25lX2ZuYW1lKQogIAogIGZhc3RjbG9uZV9pbnB1dF9zdWJzZXQgPC0gcHljbG9uZV9pbnB1dCAlPiUKICAgIGRwbHlyOjpyZW5hbWUoInZhcl9jb3VudHMiID0gImFsdF9jb3VudHMiKSAlPiUgCiAgICBmaWx0ZXIoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCA9PSBzYW1wbGVfbmFtZVt4XSkgJT4lIAogICAgI3NlbGVjdCgtYyhLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEKSkgJT4lIAogIHdyaXRlX3RzdihmaWxlLnBhdGgoZmFzdGNsb25lX2ZuYW1lKSkKICAgIAogICAgCiAgICAKfQp9CgoKIyBUbyBmaW5kIHRoZSBwb3NpdGlvbiBvZiBkdXBsaWNhdGUgZWxlbWVudHMgaW4geCwgdXNlIHRoaXM6CiMgZHVwbGljYXRlZChweWNsb25lX2lucHV0KQoKIyBFeHRyYWN0IGR1cGxpY2F0ZSBlbGVtZW50czoKIyBweWNsb25lX2lucHV0W2R1cGxpY2F0ZWQocHljbG9uZV9pbnB1dCldCgpgYGAKCiMgUGxvdCBkZXB0aCBjb3ZlcmFnZQoKYGBge3IgZGVmaW5lLXBhcmFtZXRlcnMtZm9yLXBsb3RzfQojIFJlYWQgY29sb3IgcGFsZXR0ZQpwYWxldHRlX2RmIDwtIHJlYWRyOjpyZWFkX3RzdihwYWxldHRlX2ZpbGUsIGd1ZXNzX21heCA9IDEwMDAwMCwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkgJT4lIAogIG11dGF0ZSh0dW1vcl9kZXNjcmlwdG9yID0gY29sb3JfbmFtZXMpCgojIERlZmluZSBhbmQgb3JkZXIgcGFsZXR0ZQpwYWxldHRlIDwtIHBhbGV0dGVfZGYkaGV4X2NvZGVzCm5hbWVzKHBhbGV0dGUpIDwtIHBhbGV0dGVfZGYkdHVtb3JfZGVzY3JpcHRvcgoKIyBEZWZpbmUgdGltZXBvaW50cwp0aW1lcG9pbnRzID0gYygiRGlhZ25vc2lzIiwgIlByb2dyZXNzaXZlIiwgIlJlY3VycmVuY2UiLCAiRGVjZWFzZWQiLCAiU2Vjb25kIE1hbGlnbmFuY3kiLCAiVW5hdmFpbGFibGUiKQoKYGBgCgpgYGAge3IgZGVwdGgtY292ZXJhZ2UsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA1LCBmaWcuZnVsbHdpZHRoID0gVFJVRX0KZm9yIChpIGluIDE6bGVuZ3RoKGRhdGFfZGlyKSApIHsgCiAgCiAgIyBDcmVhdGUgc2FtcGxlX25hbWUKICBzYW1wbGVfbmFtZSA8LSAgdW5pcXVlKGFzLmNoYXJhY3Rlcihnc3ViKCIuY2FsbC5jbnMiLCAiIiwgc3RyX3NwbGl0X2ZpeGVkKGRhdGFfZGlyW2ldLCAiLyIsIDEzKVssMTFdKSkpCiAgc2FtcGxlX25hbWUgPC0gc29ydChzYW1wbGVfbmFtZSwgZGVjcmVhc2luZyA9IEZBTFNFKQogIHByaW50KHNhbXBsZV9uYW1lKQogIApmb3IgKHggaW4gc2VxX2Fsb25nKHNhbXBsZV9uYW1lKSApIHsgCiAgCiAgcHljbG9uZV9pbnB1dF9zdWJzZXQgPC0gcHljbG9uZV9pbnB1dCAlPiUKICAgIGZpbHRlcihLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEID09IHNhbXBsZV9uYW1lW3hdKSAlPiUgCiAgICBzZWxlY3QoLWMoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCkpICMlPiUgCiAgICMgbXV0YXRlKHR1bW9yX2Rlc2NyaXB0b3IgPSBmYWN0b3IodHVtb3JfZGVzY3JpcHRvciksCiAgICAjICAgICB0dW1vcl9kZXNjcmlwdG9yID0gZmN0X3JlbGV2ZWwodHVtb3JfZGVzY3JpcHRvciwgdGltZXBvaW50cykpICU+JSAKICAgICNhcnJhbmdlKHR1bW9yX2Rlc2NyaXB0b3IsIHNhbXBsZV9pZCkKICAKICAjIE1ha2UgdGhpcyByZXByb2R1Y2libGUKICBzZXQuc2VlZCgyMDIzKQoKICAjIERlZmluZSBsYWJlbCBmb3IgcGxvdHMKICBUaW1lcG9pbnQgPC0gZmFjdG9yKHggPSBweWNsb25lX2lucHV0X3N1YnNldCR0dW1vcl9kZXNjcmlwdG9yLCBsZXZlbHMgPSB0aW1lcG9pbnRzKQogIAogICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiAgIyBDcmVhdGUgYnhwIHJlZl9jb3VudHMKICAgIHAgPC0gcHJpbnQoZ2dwbG90KHB5Y2xvbmVfaW5wdXRfc3Vic2V0LCBhZXMoc2FtcGxlX2lkLCByZWZfY291bnRzLCBjb2xvciA9IFRpbWVwb2ludCkpICsgCiAgICAgICAgICAgICAgICAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjE1LCBzaXplID0gMC43LCBhbHBoYSA9IDAuNikgKwogICAgICAgICAgICAgICAgIGdncGxvdDI6Omdlb21fYm94cGxvdChjb2xvciA9ICJibGFjayIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAwLjI1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvZWYgPSAwKSArICMgcmVtb3ZlIHdoaXNrZXJzCiAgICAgICAgICAgICAgICAgdGhlbWVfUHVibGljYXRpb24oKSArIAogICAgICAgICAgICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzb3J0KG5hbWVzKHBhbGV0dGUpKSkgKwogICAgICAgICAgICAgICAgICNyb3RhdGUoKSArCiAgICAgICAgICAgICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpICsKICAgICAgICAgICAgICAgICBzdGF0X3N1bW1hcnkoZnVuLnk9bWVhbixzaGFwZT0xLGNvbD0nYmxhY2snLGdlb209J3BvaW50JykgKwogICAgICAgICAgICAgICAgIGxhYnModGl0bGUgPSBzYW1wbGVfbmFtZVt4XSwKICAgICAgICAgICAgICAgICAgICAgIHggPSAic2FtcGxlX2lkIiwKICAgICAgICAgICAgICAgICAgICAgIHkgPSAicmVmX2NvdW50cyIsCiAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJUaW1lcG9pbnQiKSkKICAgIAogICAgIyBTYXZlIHRoZSBwbG90CiAgICBnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZTAoc2FtcGxlX25hbWVbeF0sICItcmVmX2NvdW50cy5wZGYiKSwgCiAgICAgICAgIHBhdGggPSBweWNsb25lX3Bsb3RzX2RpciwgCiAgICAgICAgIHdpZHRoID0gNiwgCiAgICAgICAgIGhlaWdodCA9IDUsIAogICAgICAgICBkZXZpY2UgPSAicGRmIiwgCiAgICAgICAgdXNlRGluZ2JhdHMgPSBGQUxTRSkKICAgIAoKICAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwogICMgQ3JlYXRlIGJ4cCBhbHRfY291bnRzCiAgICBwIDwtIHByaW50KGdncGxvdChweWNsb25lX2lucHV0X3N1YnNldCwgYWVzKHNhbXBsZV9pZCwgYWx0X2NvdW50cywgY29sb3IgPSBUaW1lcG9pbnQpKSArIAogICAgICAgICAgICAgICAgIGdlb21faml0dGVyKHdpZHRoID0gMC4xNSwgc2l6ZSA9IDAuNywgYWxwaGEgPSAwLjYpICsKICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjpnZW9tX2JveHBsb3QoY29sb3IgPSAiYmxhY2siLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMC4yNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2VmID0gMCkgKyAjIHJlbW92ZSB3aGlza2VycwogICAgICAgICAgICAgICAgIHRoZW1lX1B1YmxpY2F0aW9uKCkgKyAKICAgICAgICAgICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGFsZXR0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc29ydChuYW1lcyhwYWxldHRlKSkpICsKICAgICAgICAgICAgICAgICAjcm90YXRlKCkgKwogICAgICAgICAgICAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKSArCiAgICAgICAgICAgICAgICAgc3RhdF9zdW1tYXJ5KGZ1bi55PW1lYW4sc2hhcGU9MSxjb2w9J2JsYWNrJyxnZW9tPSdwb2ludCcpICsKICAgICAgICAgICAgICAgICBsYWJzKHRpdGxlID0gc2FtcGxlX25hbWVbeF0sCiAgICAgICAgICAgICAgICAgICAgICB4ID0gInNhbXBsZV9pZCIsCiAgICAgICAgICAgICAgICAgICAgICB5ID0gImFsdF9jb3VudHMiLAogICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiVGltZXBvaW50IikpCiAgICAKICAgICMgU2F2ZSB0aGUgcGxvdAogICAgZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKHNhbXBsZV9uYW1lW3hdLCAiLWFsdF9jb3VudHMucGRmIiksIAogICAgICAgICBwYXRoID0gcHljbG9uZV9wbG90c19kaXIsIAogICAgICAgICB3aWR0aCA9IDYsIAogICAgICAgICBoZWlnaHQgPSA1LCAKICAgICAgICAgZGV2aWNlID0gInBkZiIsIAogICAgICAgIHVzZURpbmdiYXRzID0gRkFMU0UpCiAgCiAgCn0KfQoKYGBgCgoKIyBUb3RhbCBudW1iZXIgb2YgbXV0YXRpb25zIGFjcm9zcyB0aW1lcG9pbnRzIGFuZCBiaW9zcGVjaW1lbiBzYW1wbGUgcGVyIFBhdGllbnQgY2FzZQoKYGBge3IgY3JlYXRlLWJhcnBsb3Qtc2FtcGxlLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNSwgZmlnLmZ1bGx3aWR0aCA9IFRSVUV9CmZvciAoaSBpbiAxOmxlbmd0aChkYXRhX2RpcikgKSB7IAogIAogICMgQ3JlYXRlIHNhbXBsZV9uYW1lCiAgc2FtcGxlX25hbWUgPC0gIHVuaXF1ZShhcy5jaGFyYWN0ZXIoZ3N1YigiLmNhbGwuY25zIiwgIiIsIHN0cl9zcGxpdF9maXhlZChkYXRhX2RpcltpXSwgIi8iLCAxMylbLDExXSkpKQogIHNhbXBsZV9uYW1lIDwtIHNvcnQoc2FtcGxlX25hbWUsIGRlY3JlYXNpbmcgPSBGQUxTRSkKICBwcmludChzYW1wbGVfbmFtZSkKICAKZm9yICh4IGluIHNlcV9hbG9uZyhzYW1wbGVfbmFtZSkgKSB7IAogIAogIHB5Y2xvbmVfaW5wdXRfc3Vic2V0IDwtIHB5Y2xvbmVfaW5wdXQgJT4lCiAgICBmaWx0ZXIoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCA9PSBzYW1wbGVfbmFtZVt4XSkgJT4lIAogICAgc2VsZWN0KC1jKEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQpKQogIAogICMgRGVmaW5lIHBhcmFtZXRlcnMgZm9yIGZ1bmN0aW9uCiAgeWxpbSA9IG1heChweWNsb25lX2lucHV0X3N1YnNldCRtdXRhdGlvbl9jb3VudCkKICAKICAKICAgIyBSZW5hbWUgbGVnZW5kIGZvciB0aW1lcG9pbnRzCiAgVGltZXBvaW50IDwtIGZhY3RvcihweWNsb25lX2lucHV0X3N1YnNldCR0dW1vcl9kZXNjcmlwdG9yKQogIAogICMgUGxvdCBzdGFja2VkIGJhcnBsb3QgCiAgcHJpbnQoZ2dwbG90KHB5Y2xvbmVfaW5wdXRfc3Vic2V0LCBhZXMoeCA9IHNhbXBsZV9pZCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gbXV0YXRpb25fY291bnQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IFRpbWVwb2ludCkpICsgIAogICAgICAgICAgICAgICBnZW9tX2NvbChwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHJldmVyc2UgPSBUUlVFKSkgKwogICAgICAgICAgICAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAwLjUpICsgCiAgICAgICAgICAgICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUsIGJyZWFrcyA9IHNvcnQobmFtZXMocGFsZXR0ZSkpKSArIAogICAgICAgICAgICAgICB0aGVtZV9QdWJsaWNhdGlvbigpICsgCiAgICAgICAgICAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gODUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2anVzdCA9IDEpKSArIAogICAgICAgICAgICAgICBsYWJzKHRpdGxlID0gcGFzdGUoc2FtcGxlX25hbWUpKSArIAogICAgICAgICAgICAgICBsYWJzKHggPSAic2FtcGxlX2lkIiwgeSA9ICJUb3RhbCBNdXRhdGlvbnMiKSArCiAgICAgICAgICAgICAgIHlsaW0oMCwgeWxpbSkpIAogIAogIAp9Cn0KYGBgCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAKCgo=